package com.commute.planner;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;

import com.commute.CommuteEvent;
import com.commute.data.DataStructure2;
import com.commute.data.Route;
import com.commute.data.Stop;
import com.commute.data.StopTime;
import com.commute.data.Trip;
import com.commute.schedule.Schedule;


public class CommutePlanner {
	int bufferTime;
	int walkDistance;
	DistanceCalculator DistCalc;
	DataStructure2 data;
	CommuteEvent comEvent;
	
	public CommutePlanner(int walkDistance, int bufferTime){
		this.walkDistance=walkDistance;
		this.DistCalc = new DistanceCalculator();
		this.data = new DataStructure2();
		this.bufferTime = bufferTime; 
	}
	

	
	public void calcTest(){		//test method
		double lonA=22.140003;
		double latA=65.618174;
		
		double lonB=22.145720999999998;
		double latB=65.619042;
		
		double distance =DistCalc.calcDistance(latA, lonA, latB, lonB);
		System.out.println(distance);
	}
	
	
	public CommuteEvent nextEvent(String scheduleData, double myLat, double myLon){
		double latSchedule=65.618174;				//hard coded for LTU schedule
		double lonSchedule=22.140003;
		
		//Schedule mySchedule = new Schedule("/Users/hedenberg/Documents/Universitet/M7012E/m7012e/M7012E.ics", latSchedule, lonSchedule );
		
		Schedule mySchedule = new Schedule(scheduleData, latSchedule, lonSchedule);
		LinkedList<RecommendedTrip> recTrips = new LinkedList<RecommendedTrip>();
		comEvent = new CommuteEvent();

		
		
		
		comEvent.setEventActivity(mySchedule.nextActivity()); 
		System.out.println("ROUTES " + data.routes.size());
		android.util.Log.println(1, "ROUTES " , data.routes.size() + "");
		//updates the Activity data in the comEvent with the 
		
//		System.out.println(comEvent.getEventActivity().lat()+" Lat");
//		System.out.println(comEvent.getEventActivity().lon()+" Lon");
		
		/*******************END *****************************/
		
		LinkedList<Stop> stopsInRangeEnd =findStopsInRange(comEvent.getEventActivity().lat() , comEvent.getEventActivity().lon());		//TODO comEvent or home coordinates instead		
		//calculates the Stops in Range of a specific coordinate given by myLat and myLon the Range is the specified variable Walkdistance
		LinkedList<Route> routesInRangeEnd = findRoutesAtLocation(stopsInRangeEnd);	
		//calculates routes In Range with help of the stops
		LinkedList<RecommendedTrip> recTripsEnd= closestStopsEnd(routesInRangeEnd);
		//add the best Stop for each route around the end of the trip
		
		/*******************START *****************************/		
//		double centrumLat =65.584177;			//test data smedjan typ....
//		double centrumLon =22.153134;			//test data
		
		double hertsonLat = 65.585155;
		double hertsonLon = 22.260994;
		
		
		LinkedList<Stop> stopsInRangeStart =findStopsInRange(hertsonLat,hertsonLon);		//TODO Own coordinates instead			
		//calculates the Stops in Range of a specific coordinate given by myLat and myLon the Range is the specified variable Walkdistance
		LinkedList<Route> routesInRangeStart = findRoutesAtLocation(stopsInRangeStart);	
		//calculates routes In Range with help of the stops		
		LinkedList<RecommendedTrip> recTripsStart= closestStopStart(routesInRangeStart);
		//add the best Stop for each route around the start of the trip
		
		/**********************COMBINE******************/
		LinkedList<RecommendedTrip> possibleTrips= combineTrips(recTripsEnd, recTripsStart);
		
		recTrips = findBestTrips(possibleTrips);
		
		comEvent.setTrips(recTrips);
		
		return comEvent;
	}
	
	private LinkedList<RecommendedTrip> findBestTrips(LinkedList<RecommendedTrip> possibleTrips){
		LinkedList<RecommendedTrip> bestTrips = new LinkedList<RecommendedTrip>();
		Calendar cal = Calendar.getInstance();
		SimpleDateFormat format = new SimpleDateFormat("HHmm");
		
		Date currentDate = new Date();
		cal.setTime(currentDate);
		cal.add(Calendar.MINUTE, bufferTime);
		currentDate=cal.getTime();
		int currentTime = Integer.parseInt(format.format(currentDate));
		currentTime = 930;
		System.out.println("currentime: "+currentTime);
		//currentTime + buffer
		
		Date eventDate = comEvent.getEventActivity().start();
		cal.setTime(eventDate);
		cal.add(Calendar.MINUTE, -bufferTime);
		eventDate = cal.getTime();
		int eventTime = Integer.parseInt(format.format(eventDate));
		System.out.println("eventTime: "+eventTime);
		//eventTime + buffer
		
		int maxFromRoute=20;
		int currentFromRoute;


		for (RecommendedTrip recTrip : possibleTrips) {
			for (int i = recTrip.getRouteEnd().getTrips().size()-1; i>=0; i--){
				Trip trip = recTrip.getRouteEnd().getTrips().get(i);
				for (StopTime st : trip.stopTimes()) {
					if(st.stopId()==recTrip.getBestEnd().getStopId()){
						int stopTime = Integer.parseInt(format.format(st.arrivalTime()));
						if(stopTime<=eventTime){
							RecommendedTrip temp = new RecommendedTrip();
							temp.setTripEnd(recTrip.getRouteEnd(), recTrip.getBestEnd());
							temp.setTripStart(recTrip.getRouteStart(), recTrip.getBestStart());
							if(recTrip.isTransfer()){
								temp.setTranfer();
							}
							temp.setEndTime(st.arrivalTime());
							temp.setTrip(trip);
							temp.setStopTimeEnd(st);
							bestTrips.add(temp);
						}
					}
				}
			}
		}
		
		
		// Start StopTime for last trip (That is _the only_ trip for non-transfer)
		LinkedList<RecommendedTrip> validTripsNonTransfer = new LinkedList<RecommendedTrip>();
		LinkedList<RecommendedTrip> tripsTransfer = new LinkedList<RecommendedTrip>();
		System.out.println(bestTrips.size());
		currentFromRoute=0;
		for (RecommendedTrip bT : bestTrips) {
			for (StopTime st : bT.getTrip().stopTimes()) {
				if(st.stopId()==bT.getBestStart().getStopId()){
					int startTime = Integer.parseInt(format.format(st.departureTime()));
					
					if(startTime >= currentTime && (st.stopSequence() < bT.getStopTimeEnd().stopSequence())){
						if(currentFromRoute < maxFromRoute){
							bT.setStartTime(st.departureTime());
							bT.stopTimeStart = st;
							validTripsNonTransfer.add(bT);
							currentFromRoute++;
						}
					}
				} else if(st.stopId()==11 && bT.transfer){
					int startTime = Integer.parseInt(format.format(st.departureTime()));
					if(startTime >= currentTime && (st.stopSequence() < bT.getStopTimeEnd().stopSequence())){
						bT.transferStartTime = st.departureTime();
						bT.stopTimeTransferStart = st;
						tripsTransfer.add(bT);
					}
				}
			}
			
		}
		
		// 
		LinkedList<RecommendedTrip> almostValidTripsTransfer = new LinkedList<RecommendedTrip>();
		for (RecommendedTrip tT : tripsTransfer) {
			for (int i = tT.getRouteStart().getTrips().size()-1; i>=0; i--){
				Trip trip = tT.getRouteStart().getTrips().get(i);
				for (StopTime st : trip.stopTimes()) {
					if(st.stopId()==11){
						int arrivalTime = Integer.parseInt(format.format(st.arrivalTime()));
						int transferStartTime = Integer.parseInt(format.format(tT.stopTimeTransferStart.departureTime()));
						if(arrivalTime<=transferStartTime){	
							
							RecommendedTrip temp = new RecommendedTrip();
							temp.setTripEnd(tT.getRouteEnd(), tT.getBestEnd());
							temp.setTripStart(tT.getRouteStart(), tT.getBestStart());
							if(tT.isTransfer()){
								temp.setTranfer();
							}
							temp.setEndTime(st.arrivalTime());
							temp.setTrip(trip);
							temp.setStopTimeEnd(st);
							bestTrips.add(temp);
							
							temp.transferEndTime = st.arrivalTime();
							temp.stopTimeTransferEnd = st;
							temp.firstTrip = trip;
							almostValidTripsTransfer.add(temp);
						}
					}
				}
			}
		}
		
		LinkedList<RecommendedTrip> validTripsTransfer = new LinkedList<RecommendedTrip>();
		for (RecommendedTrip aVTT : almostValidTripsTransfer) {
			for (StopTime st : aVTT.firstTrip.stopTimes()) {
				if(st.stopId()==aVTT.getBestStart().getStopId()){
					int departureTime = Integer.parseInt(format.format(st.departureTime()));
					if(departureTime >= currentTime && (st.stopSequence() < aVTT.stopTimeTransferEnd.stopSequence())){
						aVTT.setStartTime(st.departureTime());
						aVTT.stopTimeStart = st;
						validTripsTransfer.add(aVTT);
					}
				}
			}
		}
		
		// Combine nonTransfer and transfer
		LinkedList<RecommendedTrip> validTrips = new LinkedList<RecommendedTrip>();
		for (RecommendedTrip vTNT : validTripsNonTransfer) {
			validTrips.add(vTNT);
		}
		for (RecommendedTrip vTT : validTripsTransfer) {
			validTrips.add(vTT);
		}
		
//		System.out.println(validTrips.size());
//		for (RecommendedTrip bT : validTrips) {
//			System.out.println("start: " +bT.getStartTime()+ " "+ bT + " "+ bT.getRouteStart().getRouteName());
//			System.out.println("end: "+bT.getEndTime()+ " "+bT+ " " + bT.getRouteEnd().getRouteName());
//
//			System.out.println("");
//		}
		
		LinkedList<RecommendedTrip> validTrips2 = new LinkedList<RecommendedTrip>();
		int currentofRoute=0;
		int maxofRoute=2;
		LinkedList<Integer> routesDone = new LinkedList<Integer>();
		for (RecommendedTrip bT : validTrips) {
			if(!routesDone.contains(bT.routeEnd.getRouteId())){
				if(currentofRoute<maxofRoute){
					validTrips2.add(bT);
					currentofRoute++;
				}else{
					routesDone.add(bT.routeEnd.getRouteId());
					currentofRoute=0;
					
				}
			}
		}
		
		
//		System.out.println(validTrips2.size());
//		System.out.println("correct??");
		for (RecommendedTrip bT : validTrips2) {
			System.out.println("start: " +bT.getStartTime()+ " "+ bT + " "+ bT.getRouteStart().getRouteName());
			System.out.println("end: "+bT.getEndTime()+ " "+bT+ " " + bT.getRouteEnd().getRouteName());

			System.out.println("");
		}
		
		
		return validTrips2;
	}
	
	private LinkedList<RecommendedTrip> combineTrips(LinkedList<RecommendedTrip> recTripsEnd, LinkedList<RecommendedTrip> recTripsStart){
		LinkedList<RecommendedTrip> combinedTrips = new LinkedList<RecommendedTrip>();
		LinkedList<RecommendedTrip> tempEnd = new LinkedList<RecommendedTrip>();
		for (RecommendedTrip start : recTripsStart) {
			for (RecommendedTrip end : recTripsEnd) {								//TODO works? I don't know??!?!?
				if(start.getRouteStart().equals(end.getRouteEnd())){
					start.setTripEnd(end.getRouteEnd(), end.getBestEnd());
					combinedTrips.add(start);
					tempEnd.add(end);
				}
			}
		}
		
		recTripsStart.removeAll(combinedTrips);
		recTripsEnd.removeAll(tempEnd);
		
		for (RecommendedTrip start : recTripsStart) {
			for (RecommendedTrip end : recTripsEnd) {	
				RecommendedTrip temp =new RecommendedTrip();
				temp.setTripStart(start.getRouteStart(), start.getBestStart());
				temp.setTripEnd(end.getRouteEnd(), end.getBestEnd());
				temp.setTranfer();
				combinedTrips.add(temp);
			}
		}
		/****************PRINT*******************/
		System.out.println("Test Combine");
		System.out.println(combinedTrips.size());
		for (RecommendedTrip recTrips : combinedTrips) {
			System.out.println("Start: ("+recTrips.getBestStart().getStopName()+"on Route: "+recTrips.getRouteStart().getRouteName()+")       End: ("+recTrips.getBestEnd().getStopName()+" on Route: "+ recTrips.getRouteEnd().getRouteName()+")");
		}
		return combinedTrips;
	}
	
	
	private LinkedList<RecommendedTrip> closestStopStart(LinkedList<Route> routesInRange){
		LinkedList<RecommendedTrip> recTrips = new LinkedList<RecommendedTrip>();
		for (Route route : routesInRange) {
			double minDistance =route.getStops().get(0).getCurrentDistance();
			Stop bestStop = route.getStops().get(0);
			for (Stop stop : route.getStops()) {
				if (minDistance> stop.getCurrentDistance()) {
					minDistance = stop.getCurrentDistance();
					bestStop = stop;
				}
			}
			RecommendedTrip temp = new RecommendedTrip();
			temp.setTripStart(route, bestStop);
			recTrips.add(temp);
			

		}
		
		for (RecommendedTrip rec : recTrips) {
			System.out.println("Route: "+rec.getRouteStart().getRouteName()+" Stop: "+rec.getBestStart().getStopName());
		}
		System.out.println("");
		
		return recTrips;
	}
	
	
	
	private LinkedList<RecommendedTrip> closestStopsEnd(LinkedList<Route> routesInRange){
		LinkedList<RecommendedTrip> recTrips = new LinkedList<RecommendedTrip>();
		for (Route route : routesInRange) {
			double minDistance =route.getStops().get(0).getCurrentDistance();
			Stop bestStop = route.getStops().get(0);
			for (Stop stop : route.getStops()) {
				if (minDistance> stop.getCurrentDistance()) {
					minDistance = stop.getCurrentDistance();
					bestStop = stop;
				}
			}
			RecommendedTrip temp = new RecommendedTrip();
			temp.setTripEnd(route, bestStop);
			recTrips.add(temp);
		}
		
		for (RecommendedTrip rec : recTrips) {
			System.out.println("Route: "+rec.getRouteEnd().getRouteName()+" Stop: "+rec.getBestEnd().getStopName());
		}
		System.out.println("");
		return recTrips;
	}
	
	
	private LinkedList<Route> findRoutesAtLocation(LinkedList<Stop> stopsInRange){
		LinkedList<Integer> activeRouteIds = new LinkedList<Integer>();
		LinkedList<Integer> stopRoutes = new LinkedList<Integer>();
		for (Stop s : stopsInRange) {
			stopRoutes= s.getRoutes();
			for (Integer i : stopRoutes) {
				if (!activeRouteIds.contains(i)){
					activeRouteIds.add(i);
				}
			}
		}
		LinkedList<Route> activeRoutes = new LinkedList<Route>();
		for (Route route : data.routes) {
			for (Integer i : activeRouteIds) {
				Integer routeId = route.getRouteId();
				if (routeId.equals(i)) {
					activeRoutes.add(route);
				}
			}
		}
		/****************PRINT************************/
		for (Route route : activeRoutes) {
			System.out.println(route.getRouteName());
			for (Stop s : route.getStops()) {
				if(s.getCurrentDistance()<walkDistance){
					System.out.println(s.getStopName());
				}
				
			}
			System.out.println("");
		}
		/*********************************************/
		return activeRoutes;
	}
	
	
	
	private LinkedList<Stop> findStopsInRange(double locationLAT, double locationLON){
		LinkedList<Stop> stops = data.stops;
		LinkedList<Stop> stopsInRange = new LinkedList<Stop>();
		for (Stop s : stops) {
			double distance =DistCalc.calcDistance(locationLAT, locationLON, s.getLat(), s.getLon());
//			System.out.print(s.getStopId()+": "+distance+" Routes: ");
			s.setCurrentDistance(distance);
			if(distance < walkDistance){														//
				stopsInRange.add(s);
			}
			
			//System.out.print(s.getStopId()+": "+s.getCurrentDistance() +" Routes: ");
			
//			for (int i = 0; i < s.getRoutes().size(); i++) {
//				System.out.print(s.getRoutes().get(i)+" ");
//			}
//			System.out.println(" ");
		}
		
		for (Stop stop : stopsInRange) {
			System.out.print(stop.getStopId()+": "+stop.getCurrentDistance()+" Routes: ");
			for (int i = 0; i < stop.getRoutes().size(); i++) {
				
				System.out.print(stop.getRoutes().get(i)+" ");
			}
			System.out.println(" ");
		}
		
		return stopsInRange;
	}
	
}
